package com.ning.city.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ning.city.bean.Token;
import com.ning.city.service.UserService;
import com.ning.city.vo.CommonResult;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;

/**
 * 初步验证 token
 * <p>
 * Error Status
 * 441: "用户未登录",
 * 442: "Token 已过期",
 * 443: "Token 有误",
 * </p>
 *
 * @author Ning 242741154@qq.com
 * @date 2021/1/7 15:56
 */
@Component
@WebFilter(urlPatterns = "/*")
public class AuthFilter extends HttpFilter {
    private final ObjectMapper mapper;
    private final RedisTemplate<String, String> redisTemplate;

    public AuthFilter(ObjectMapper mapper, RedisTemplate<String, String> redisTemplate) {
        this.mapper = mapper;
        this.redisTemplate = redisTemplate;
    }

    public static final String[] URL_SKIP_LIST
            = new String[]{
            "login", "register", "echo", "hello",
            "hello1", "hello2"
    };

    private static final String TOKEN_HEADER_NAME = "X-token";

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=utf-8");

        final String url = request.getRequestURL().toString();
        // 当请求为"登录"或"注册"等时, 放过 token 检查
        for (String s : URL_SKIP_LIST) {
            if (url.endsWith(s)) {
                chain.doFilter(request, response);
                return;
            }
        }
        final String tokenString = request.getHeader(TOKEN_HEADER_NAME);
        if (tokenString == null || "".equals(tokenString)) {
            errorResult(response.getWriter(), CommonResult.UN_LOGIN, "用户未登录!");
        } else {
            try {
                Token token = mapper.readValue(tokenString, Token.class);
                final String userId = token.getUserId();
                String tokenFromRedis = redisTemplate.opsForValue()
                        .get(UserService.USER_REDIS_PREFIX + userId);
                if (tokenFromRedis == null) {
                    errorResult(response.getWriter(), CommonResult.TOKEN_EXPIRE, "Token 已过期, 请重新登录!");
                    return;
                }
                Token token2 = mapper.readValue(tokenFromRedis, Token.class);
                if (token.equals(token2)) {
                    chain.doFilter(request, response);
                } else {
                    throw new Exception();
                }
            } catch (Exception e) {
                errorResult(response.getWriter(), CommonResult.TOKEN_ERROR, "Token 有误!");
            }
        }
    }

    private void errorResult(PrintWriter pw, int code, String msg) throws IOException {
        final CommonResult<?> result = new CommonResult<>();
        result.setCode(code);
        result.setErrors(Collections.singletonList(msg));
        mapper.writeValue(pw, result);
    }
}
